home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / pctchnqs / 1990 / number3 / antivenm.asm < prev    next >
Assembly Source File  |  1990-04-15  |  12KB  |  291 lines

  1. ;***********************************************************************
  2. ;   Listing 1 - ANTIVENM.ASM
  3. ;
  4. ;   Written by Kevin D. Weeks 10-8-89
  5. ;   Released to the Public Domain.
  6. ;
  7. ;   int     anti_venom(int buf_size,char* buffer)
  8. ;
  9. ;   Description:
  10. ;       This collection of routines performs a Cyclic Redundancy Check on
  11. ;       the program they are a part of and compares the resulting value
  12. ;       with a value known to be correct. A descrepancy in these two num-
  13. ;       bers indicates the program has been modified and may be infected
  14. ;       by a virus.
  15. ;
  16. ;   Returns:
  17. ;       -1      Program has been modified and may be infected.
  18. ;        0      No problems detected.
  19. ;       +1      Execution error (file not found, PSP not found, etc.)
  20. ;
  21. .MODEL  small
  22. if @codesize
  23.     bp_ofs  equ 6
  24. else
  25.     bp_ofs  equ 4
  26. endif
  27.  
  28. FOUND           equ     0
  29. EXECUTION_ERR   equ     1
  30. VIRUS_DETECTED  equ     -1
  31.  
  32. .DATA
  33. state   dw      ?                       ; current state of valid_crc search
  34. cur_crc dw      ?                       ; current crc value
  35.  
  36. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  37. .CODE
  38.     public  _anti_venom
  39.  
  40. ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  41. ;   The _anti_venom function performs the initial setup (such as ac-
  42. ;   quiring the file name) and then successively calls READ_FILE and
  43. ;   CALC_CRC until the entire file has been processed.
  44. ;
  45. if @codesize                            ; if large code memory model
  46. _anti_venom  proc    far
  47. else                                    ; else, small code memory model
  48. _anti_venom  proc    near
  49. endif
  50.     push    bp                          ; save the caller's stack frame
  51.     mov     bp,sp
  52.     push    bx
  53.     push    cx
  54.     push    dx
  55.     push    di
  56.     push    si
  57.     push    es
  58.     push    ds
  59.     jmp     STEP_1                      ; jump over the CRC and signature
  60.  
  61. ; the signature and validation crc are stored here to guarantee they'll
  62. ; be contiguous and not subject to compiler startup manipulation.
  63. signature   db  'ANTIV'
  64. valid_crc   dw  0
  65.  
  66. ; the first step is to get the psp of this process.
  67. STEP_1:
  68.     call    GET_PSP                         ; returns with BX = PSP
  69.     cmp     bx,0                            ; check for a 0 return
  70.     je      ERROR_OUT
  71.  
  72. ; from the psp we get the address of the environment block which con-
  73. ; tains the path and name of this program.
  74.     call    SCAN_ENVIRONMENT
  75.  
  76. ; SCAN_ENVIRONMENT returns with DS:DX pointing to file path and name
  77. ; ready for a call to DOS to open the file.
  78.     mov     ax,3d00h                    ; Open File function - read only
  79.     int     21h                         ; call DOS
  80.     jc      ERROR_OUT
  81.  
  82. ; get the buffer address form the stack. NOTE: this is language specific!
  83.     mov     bx,ax                       ; move file handle to bx
  84.     mov     cx,[bp + bp_ofs]            ; get buffer size
  85.     mov     dx,[bp + bp_ofs + 2]        ; get offset address of buffer
  86. if @datasize                            ; if large data model
  87.     mov     ds,[bp + bp_ofs + 4]        ; get segment address of buffer
  88. else                                    ; else small data model
  89.     pop     ds                          ; restore original data segment
  90.     push    ds                          ; keep stack accurate
  91. endif
  92.     call    CLEAR_OUT                   ; initialize the state
  93.  
  94. ; this is where the real work gets done. we read to the end of the file
  95. ; calling CALC_CRC after each read.
  96. MAIN_LOOP:
  97.     call    READ_FILE                   ; read the file
  98.     jc      ERROR_OUT                   ; bailout if read error
  99.     cmp     ax,0                        ; check for EOF
  100.     jz      COMPARE_CRC                 ; if so, complete testing
  101.                                         ; else
  102.     push    cx                          ; save buffer size
  103.     mov     cx,ax                       ; place bytes actually read in cx
  104.     call    CALC_CRC                    ; calculate CRC
  105.     pop     cx                          ; restore buffer size
  106.     jmp     MAIN_LOOP                   ; and repeat
  107. ; end of MAIN_LOOP
  108.  
  109. ; test the crc just calculated by subtracting it from the valid crc. if
  110. ; the result is 0 then we have our return code, otherwise we indicate a
  111. ; virus was detected
  112. COMPARE_CRC:
  113.     mov     ax,cs:valid_crc             ; move the correct crc to ax
  114.     sub     ax,cs:cur_crc               ; and subtract the new crc
  115.     jz      CLOSE                       ; if the result is 0 they're equal
  116.                                         ; else
  117.     mov     ax,VIRUS_DETECTED           ; indicate crc error
  118.     jmp     short CLOSE                 ; and bailout
  119.  
  120. ERROR_OUT:
  121.     mov     ax,EXECUTION_ERR            ; indicate execution error
  122.  
  123. CLOSE:
  124.     push    ax                          ; save the return code
  125.     mov     ax,3e00h                    ; Close File function
  126.     int     21h                         ; call DOS
  127.     pop     ax                          ; restore the return code
  128.  
  129. EXIT:
  130.     pop     ds                          ; restore the caller's environment
  131.     pop     es
  132.     pop     si
  133.     pop     di
  134.     pop     dx
  135.     pop     cx
  136.     pop     bx
  137.     mov     sp,bp
  138.     pop     bp
  139.     ret
  140. _anti_venom  endp
  141.  
  142. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  143. ;   GET_PSP
  144. ;   First we issue a call to DOS function 62h, Get PSP. this function only
  145. ;   exists in DOS 3.xx but IS documented. If we get a 0 back in register bx
  146. ;   then we assume we're running under DOS 2.xx and issue a call to the UN-
  147. ;   documented DOS function 51h.
  148. ;
  149. GET_PSP     proc    near
  150.     mov     ax,6200h                    ; DOS Get PSP function
  151.     int     21h                         ; call DOS
  152.     cmp     bx,0                        ; check for a 0 return
  153.     jz      GET_PSP2
  154.     ret                                 ; if not, return
  155. GET_PSP2:
  156.     mov     ax,5100h                    ; undocumented Get PSP function
  157.     int     21h                         ; call DOS
  158.     ret
  159. GET_PSP     endp
  160.  
  161. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  162. ;   SCAN_ENVIRONMENT
  163. ;   The DOS environment blocks consist of a series of ASCIIz strings. The
  164. ;   last string is double terminated, followed by the number of additional
  165. ;   strings(?), followed by the ASCIIz path and filename of the current
  166. ;   process, which is what we want.
  167. ;
  168. SCAN_ENVIRONMENT    proc    near
  169.     mov     si,002ch            ; offset of pointer to environment block
  170.     mov     es,bx                       ; move psp segment into es
  171.     mov     ds,es:[si]                  ; move environment segment into ds
  172.     mov     si,0                        ; set index to beginning
  173.     mov     cx,0                        ; use cx to count NUL-terminators
  174. SCAN_0:
  175.     cmp     byte ptr [si],0             ; check for a zero
  176.     jnz     SCAN_1                      ; no zero so check next character
  177.                                         ; else
  178.     inc     cx                          ; increment cx
  179.     inc     si                          ; point to next character
  180.     cmp     cx,2                        ; check to see if we've got two 0's
  181.     je      SCAN_2                      ; we do so exit loop
  182.                                         ; else
  183.     jmp     SCAN_0                      ; loop again
  184. SCAN_1:
  185.     inc     si                          ; point to next character
  186.     mov     cx,0                        ; re-initialize cx
  187.     jmp     SCAN_0                      ; and loop again
  188.  
  189. SCAN_2:                                 ; we've found the double NUL
  190.     inc     si                          ; skip over the string count
  191.     inc     si
  192.     mov     dx,si                       ; ds:dx = pointer to this program's
  193.                                         ;     path and name
  194.     ret                                 ; return
  195. SCAN_ENVIRONMENT    endp
  196.  
  197. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  198. ;   READ_FILE
  199. ;
  200. READ_FILE   proc    near
  201.     mov     ax,3f00h                    ; Read File function
  202.     int     21h                         ; call DOS
  203.     ret                                 ; return immediately
  204. READ_FILE   endp
  205.  
  206. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  207. ;   CALC_CRC
  208. ;   This routine consists of two loops. The outer one processes the input
  209. ;   buffer byte-by-byte while the inner loop calculates the crc on each
  210. ;   byte.
  211. ;
  212. CALC_CRC    proc    near
  213.     mov     si,dx                           ; move pointer to buffer to si
  214. FOR_1:
  215.     push    cx                              ; save buffer count
  216.     mov     ah,[si]                         ; move character to ah
  217.     mov     al,0                            ; clear low byte of ax
  218.     xor     ax,cs:cur_crc                   ; combine character with crc
  219.     mov     cx,8                            ; set cx to 8 for 'for' loop
  220. FOR_2:
  221.     test    ax,8000h                        ; see if MSB is on
  222.     jz      CCRC_2                          ; if not, perform else
  223.     shl     ax,1                            ; shift left one
  224.     xor     ax,1021h                        ; and incorporate prime
  225.     loop    FOR_2                           ; loop back
  226.     jmp short   CCRC_3                      ; we finished the crc calculation
  227. CCRC_2:                                     ; else
  228.     shl     ax,1                            ; shift left one
  229.     loop    FOR_2                           ; and loop
  230.  
  231. CCRC_3:
  232.     pop     cx                              ; restore buffer length to cx
  233.     cmp     cs:state,FOUND              ; see if we've found the signature
  234.     je      END_OF_FOR2                     ; if so, go on
  235.     call    FIND_VALID_CRC                  ; else, check this character
  236. END_OF_FOR2:
  237.     inc     si                              ; point to next byte
  238.     mov     cs:cur_crc,ax                   ; save current crc
  239.     loop    FOR_1                           ; and loop
  240.  
  241.     ret
  242. CALC_CRC    endp
  243.  
  244. ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  245. ;   FIND_VALID_CRC
  246. ;   This is implemented as a finite state machine (I use these things ALL
  247. ;   the time) where each state is represented by the address of the state
  248. ;   processor. This would of course have to be modified for a different
  249. ;   signature. Alternatively, it could be handled the way SETCRC.C does it
  250. ;   which is more general but less clear. My feeling is that since you
  251. ;   should change the signature for each program you use this in you might
  252. ;   as well change this routine also. It's simple enough to do.
  253. ;
  254. FIND_VALID_CRC  proc    near
  255.     jmp     cs:state
  256. FIND_A:
  257.     cmp     byte ptr [si],'A'               ; if target value not seen
  258.     jne     CLEAR_OUT                       ; go back to beginning
  259.     mov     cs:state,offset FIND_N          ; else, change state
  260.     ret
  261. FIND_N:
  262.     cmp     byte ptr [si],'N'
  263.     jne     CLEAR_OUT
  264.     mov     cs:state,offset FIND_T
  265.     ret
  266. FIND_T:
  267.     cmp     byte ptr [si],'T'
  268.     jne     CLEAR_OUT
  269.     mov     cs:state,offset FIND_I
  270.     ret
  271. FIND_I:
  272.     cmp     byte ptr [si],'I'
  273.     jne     CLEAR_OUT
  274.     mov     cs:state,offset FIND_V
  275.     ret
  276. FIND_V:
  277.     cmp     byte ptr [si],'V'
  278.     jne     CLEAR_OUT
  279.     mov     cs:state,FOUND              ; we've found the signature
  280.     dec     cx                          ; so increment the byte counter
  281.     dec     cx                          ;   past it
  282.     inc     si                          ; and increment the pointer
  283.     inc     si                          ;   past it
  284.     ret
  285. CLEAR_OUT:
  286.     mov     cs:state,offset FIND_A
  287.     ret
  288. FIND_VALID_CRC  endp
  289.  
  290.     END
  291.